#ifndef __FACEBOOKASYNC_H__
#define __FACEBOOKASYNC_H__

//=====================
//Facebook client class (asynchronious)
//by Alexander Samarin
//
//v 0.1
//=====================

#include "facebook_new.h"


enum FACEBOOK_TRANSFERSTATUS
{
	TRANSFERSTATUS_NOTSTARTED,
	TRANSFERSTATUS_INPROGRESS,
	TRANSFERSTATUS_FAILED,
	TRANSFERSTATUS_SUCCEED
};

enum FACEBOOK_LOGINSTAGES
{
	LOGINSTAGE_NOTSTARTED,
	LOGINSTAGE_GETTINGAUTHENTICATION,
	LOGINSTAGE_GETTINGLOGINPAGE,
	LOGINSTAGE_POSTINGLOGINPAGE,
	LOGINSTAGE_POSTINGDESKTOPAPPCONFIRM,
	LOGINSTAGE_GETTINGFINALDESKTOPAPPPAGE,
	LOGINSTAGE_POSTINGCREATESESSION,
	LOGINSTAGE_SUCCEED,
	LOGINSTAGE_FAILED
};

enum FACEBOOK_INFOSTAGES
{
	INFOSTAGE_NOTSTARTED,
	INFOSTAGE_GETTINGUSERINFO,
	INFOSTAGE_GETTINGUSERPHOTOSIZE,
	INFOSTAGE_GETTINGFRIENDS,
	INFOSTAGE_GETTINGFRIENDSINFO,
	INFOSTAGE_GETTINGFRIENDSPHOTOSIZES,
	INFOSTAGE_SUCCEED,
	INFOSTAGE_FAILED
};

enum FACEBOOK_PHOTOSTAGES
{
	PHOTOSTAGE_NOTSTARTED,
	PHOTOSTAGE_GETTINGUSERPHOTO,
	PHOTOSTAGE_GETTINGFRIENDSPHOTOS,
	PHOTOSTAGE_SUCCEED,
	PHOTOSTAGE_FAILED
};


enum FACEBOOK_NOTIFICATIONSTAGES
{
	NOTIFSTAGE_NOTSTARTED,
	NOTIFSTAGE_INPROGRESS,
	NOTIFSTAGE_SUCCEED,
	NOTIFSTAGE_FAILED
};


enum FACEBOOK_POSTFEEDITEMSTAGES
{
	POSTFEEDSTAGE_NOTSTARTED,
	POSTFEEDSTAGE_INPROGRESS,
	POSTFEEDSTAGE_SUCCEED,
	POSTFEEDSTAGE_FAILED
};


const float FACEBOOK_TIMEOUT = 60.0f * 4.0f;

struct FacebookNotification
{
	std::list<std::string> m_UserIDs;
	std::string m_NotificationFBML;

	FacebookNotification(const std::list<std::string>& UserIDs, const std::string& NotificationFBML)
	{
		m_UserIDs = UserIDs;
		m_NotificationFBML = NotificationFBML;
	};

	FacebookNotification()
	{
		m_UserIDs.clear();
		m_NotificationFBML = "";
	};

};

struct FacebookNewsFeedItem
{
	FACEBOOK_FEEDITEM m_ItemType;
	FacebookFeedItem  m_FeedItemInfo;

	FacebookNewsFeedItem(FACEBOOK_FEEDITEM ItemType, const FacebookFeedItem& FeedItemInfo)
	{
		m_ItemType = ItemType;
		m_FeedItemInfo = FeedItemInfo;
	};

	FacebookNewsFeedItem() :
	m_FeedItemInfo()
	{
		m_ItemType = FBFEEDITEM_STORY;
	};
};


struct FacebookGenericCallData
{
	std::string		m_Method;
	std::list<std::string> m_Params;
	std::list<std::string> m_NormalizedParams;

	FacebookGenericCallData()
	{
		m_Method = "";
		m_Params.clear();
		m_NormalizedParams.clear();
	};
};




struct FacebookAsyncUser
{

	//constructor
	FacebookAsyncUser(const std::string& UserEmail, const std::string& UserPassword) :
	m_UserProfileInfo()
	{
		//curl easy init
		m_pCurl = curl_easy_init();
		assert(m_pCurl != NULL);

		//curl multi init
		m_pMultiCurl = curl_multi_init();
		assert(m_pMultiCurl != NULL);

		//members setup
		m_UserEmail = UserEmail;
		m_UserPassword = UserPassword;

		m_LoginToken = "";
		m_LoginSession = "";
		m_SessionSecret = "";

		m_UserID = "";

		m_HasActiveRequest = false;

		//friends data
		m_FriendsIDs.clear();
		m_FriendsProfileInfo.clear();
		m_AppFriendsIndexes.clear();
		m_NonAppFriendsIndexes.clear();

		//photos
		m_UserPhoto.clear();
		m_FriendsPhotos.clear();  

		//login
		m_CurLoginStage = LOGINSTAGE_NOTSTARTED; 

		//info
		m_CurInfoStage = INFOSTAGE_NOTSTARTED;

		//photos
		m_CurPhotoStage = PHOTOSTAGE_NOTSTARTED;

		//cur notification stage
		m_CurNotificationStage = NOTIFSTAGE_NOTSTARTED;

		//cur post feed item stage
		m_CurPostFeedItemStage = POSTFEEDSTAGE_NOTSTARTED; 

		//TODO Autofetch is always on now. Maybe add turning it on/off manually
		m_AutoFetch = true;

		//generic call stage
		m_GenericCallInProgress = false;

		m_DelayedNotifications.clear();
		m_DelayedNewsFeedItems.clear();
		m_DelayedGenericCalls.clear(); 

		//cur request result
		m_CurRequestResult = "";

		//buffers to hold data for transfers
		m_BUFFER_ApiCallParams = "";
		m_BUFFER_LoginGetRequestURL = "";
		m_BUFFER_LoginPOSTParams = "";
		m_BUFFER_PhotoGetSizeURL = "";

		//current transfer time
		m_CurTransferTime = 0.0f;

		//current app approve URL
		m_CurApproveURL = "";


	};

	//destructor
	~FacebookAsyncUser()
	{
		CancelCurrentRequest();

		m_DelayedNotifications.clear();
		m_DelayedNewsFeedItems.clear();

		//curl easy cleanup
		curl_easy_cleanup(m_pCurl);

		//curl multi cleanup
		curl_multi_cleanup(m_pMultiCurl);
	};

	//METHODS
	bool StartLogin();

	bool StartFetchingInfo();

	bool StartFetchingPhotos();

	bool SendNotification(std::list<std::string>& UserIDs, const std::string& NotificationFBML);

	bool PostFeedItem(FACEBOOK_FEEDITEM ItemType, const FacebookFeedItem& FeedItemInfo);

	bool MakeGenericCall(FacebookGenericCallData& CallData);

	void OnNotificationDone();

	void OnPostFeedItemDone();

	void OnGenericCallDone();

	//login
	void LoginStartStage_Authentication();
	void LoginStartStage_GettingLoginPage();
	void LoginStartStage_PostingLoginPage();
	void LoginStartStage_PostingDesktopAppConfirm();
	void LoginStartStage_GettingFinalDesktopAppPage();
	void LoginStartStage_PostingCreateSession();

	void LoginOnStageDone_Authentication();
	void LoginOnStageDone_GettingLoginPage();
	void LoginOnStageDone_PostingLoginPage();
	void LoginOnStageDone_PostingDesktopAppConfirm();
	void LoginOnStageDone_GettingFinalDesktopAppPage();
	void LoginOnStageDone_PostingCreateSession();

	//info
	void InfoStartStage_GettingUserInfo();
	void InfoStartStage_GettingUserPhotoSize();
	void InfoStartStage_GettingFriends();
	void InfoStartStage_GettingFriendsInfo();
	void InfoStartStage_GettingFriendsPhotoSizes();

	void InfoContinueStage_GettingFriendsPhotoSizes();

	void InfoOnStageDone_GettingUserInfo();
	void InfoOnStageDone_GettingUserPhotoSize();
	void InfoOnStageDone_GettingFriends();
	void InfoOnStageDone_GettingFriendsInfo();
	void InfoOnStageDone_GettingFriendsPhotoSizes();

	//photo
	void PhotoStartStage_GettingUserPhoto();
	void PhotoStartStage_GettingFriendsPhotos();

	void PhotoContinueStage_GettingFriendsPhotos();

	void PhotoOnStageDone_GettingUserPhoto();
	void PhotoOnStageDone_GettingFriendsPhotos();


	//update
	void Update(float dt);

	//pushing new request to multi
	void PushNewRequest();
	void PullCompletedRequest();

	//cancel current request
	void CancelCurrentRequest();

	void ProcessDelayedItems();


	//MEMBERS

	//curl handle
	CURL* m_pCurl;
	CURLM* m_pMultiCurl;

	//auto-fetch
	bool m_AutoFetch;

	//user email and password
	std::string						 m_UserEmail;
	std::string						 m_UserPassword;

	//login stage
	FACEBOOK_LOGINSTAGES			 m_CurLoginStage;

	//info stage
	FACEBOOK_INFOSTAGES				 m_CurInfoStage;

	//photos stage
	FACEBOOK_PHOTOSTAGES			 m_CurPhotoStage;

	//notification stage
	FACEBOOK_NOTIFICATIONSTAGES      m_CurNotificationStage;

	//post feed stage
	FACEBOOK_POSTFEEDITEMSTAGES      m_CurPostFeedItemStage;

	//generic call stage
	bool							 m_GenericCallInProgress;

	//cur friend to process (in fetching photo size and photo image)
	int								 m_CurFriendToProcess;

	//login related strings
	std::string						 m_LoginToken;
	std::string						 m_LoginSession;
	std::string						 m_SessionSecret;

	//user ID
	std::string						 m_UserID;

	//profile info
	FacebookProfileInfo				 m_UserProfileInfo;

	//friends info
	std::vector<std::string>		 m_FriendsIDs;
	std::vector<FacebookProfileInfo> m_FriendsProfileInfo;

	//app and non-app friends
	std::vector<int>				 m_AppFriendsIndexes;
	std::vector<int>				 m_NonAppFriendsIndexes;

	//photos
	std::string						 m_UserPhoto;
	std::vector<std::string>		 m_FriendsPhotos;



	//cur request result string
	std::string						 m_CurRequestResult;

	//is having active request
	bool							 m_HasActiveRequest;


	//buffers to hold data for transfers
	std::string						 m_BUFFER_ApiCallParams;
	std::string						 m_BUFFER_LoginGetRequestURL;
	std::string						 m_BUFFER_LoginPOSTParams;
	std::string						 m_BUFFER_PhotoGetSizeURL;

	//current transfer time
	float							 m_CurTransferTime;

	//delayed notifications
	std::vector<FacebookNotification> m_DelayedNotifications;

	//delayed news feed items
	std::vector<FacebookNewsFeedItem> m_DelayedNewsFeedItems;

	std::vector<FacebookGenericCallData> m_DelayedGenericCalls;

	//current desktop app approve URL
	std::string							m_CurApproveURL;

};//struct FacebookAsyncUser



class FacebookAsync
{
public:

	//Constructor and destructor
	FacebookAsync();
	~FacebookAsync();

	//Instance
	static FacebookAsync&				Instance()
	{
		//static FacebookAsync		Inst;
		//return Inst;

		if (m_pInst == NULL)
			m_pInst = new FacebookAsync();

		return *m_pInst;
	}

	static void FinalCleanup()
	{
		if (m_pInst != NULL)
		{
			delete m_pInst;
			m_pInst = NULL;
		}
	}

	static FacebookAsync* m_pInst;

	static int ConstructorTimes;
	static int DestructorTimes;

	//curl write callback function
	static size_t Write_Callback(void *ptr, size_t size, size_t nmemb, void *userp);

	//making callback - must not be called from client app
	void MakeCallback();

	//Init method
	bool Init(const std::string& AppKey, const std::string& AppSecret, const std::string& ApiServer = "api.facebook.com/restserver.php", const std::string& LoginURL = "http://api.facebook.com/login.php");

	//Update method
	void Update(float dt);

	//working with users
	bool SetCurrentUser(const std::string& UserEmail, const std::string& UserPassword);
	bool RemoveCurrentUser(); //useful when user can't login because of invalid email or password.

	//working with cur user
	bool CurUser_StartLogin();

	bool CurUser_StartFetchingInfo();

	bool CurUser_StartFetchingPhotos();

	FACEBOOK_LOGINSTAGES CurUser_GetLoginStage();
	FACEBOOK_INFOSTAGES  CurUser_GetInfoStage();
	FACEBOOK_PHOTOSTAGES CurUser_GetPhotoStage();
	FACEBOOK_NOTIFICATIONSTAGES CurUser_GetNotificationStage();
	FACEBOOK_POSTFEEDITEMSTAGES CurUser_GetPostFeedItemStage();

	bool CurUser_SetAutoFetch(bool AutoFetch);

	//notification sending routine
	bool CurUser_SendNotification(std::list<std::string>& UserIDs, const std::string& NotificationFBML);

	//feed item posting routine
	bool CurUser_PostFeedItem(FACEBOOK_FEEDITEM ItemType, const FacebookFeedItem& FeedItemInfo);

	//generic call
	bool CurUser_MakeGenericCall(FacebookGenericCallData& CallData);

	//get data routines
	bool CurUser_GetUserID(std::string& Result);
	bool CurUser_GetUserInfo(FACEBOOK_PROFILEINFO InfoField, std::string& Result);

	int	CurUser_GetFriendsCount();

	bool CurUser_GetFriendID(int FriendIndex, std::string& Result);
	bool CurUser_GetFriendInfo(int FriendIndex, FACEBOOK_PROFILEINFO InfoField, std::string& Result);

	//getting photo data routines
	int CurUser_UserPhoto_GetSize();
	const std::string* CurUser_UserPhoto_GetData();

	int CurUser_FriendPhoto_GetSize(int FriendIndex);
	const std::string* CurUser_FriendPhoto_GetData(int FriendIndex);

	//getting photo size routines
	int CurUser_UserPhoto_GetWidth();
	int CurUser_UserPhoto_GetHeight();

	int CurUser_FriendPhoto_GetWidth(int FriendIndex);
	int CurUser_FriendPhoto_GetHeight(int FriendIndex);

	//app and non-app friends routines
	int CurUser_AppFriends_GetCount();
	int CurUser_AppFriends_GetCommonIndex(int AppFriendIndex);
	
	int CurUser_NonAppFriends_GetCount();
	int CurUser_NonAppFriends_GetCommonIndex(int NonAppFriendIndex);


	//Setting Callback func
	void SetCallbackFunc(FacebookCallbackFunc pFunc) {m_pCallbackFunc = pFunc;};


	//adding facebook app method
	bool AddApplicationToProfile(const std::string& AppToAddKey, bool AllowProfile = true, bool AllowNavLink = true, bool AllowFeeds = true, bool AllowProfileActions = true);


	//If facebook site changed
	bool IsFacebookSiteChanged() {return m_FacebookSiteChanged;};

	//If login/password was wrong during last login call
	bool IsInvalidLoginPassword() {return m_InvalidLoginPassword;};

	const std::string& GetErrorDescription(){return m_ErrorDescription;};
	const std::string& GetLastFailedRequest(){return m_LastFailedRequest;};

	//setting last error params
	void SetErrorDescription(const std::string& NewErrorDesc){m_ErrorDescription = NewErrorDesc;};
	void ReportFacebookSiteChanged() {m_FacebookSiteChanged = true;};
	void ReportInvalidLoginPassword(){m_InvalidLoginPassword = true;};
	void SetLastFailedRequest(const std::string& RequestResult) {m_LastFailedRequest = RequestResult;};

	//helper methods
	void HelperRequest_MakeMD5(const std::string& SourceString, std::string& Result);
	void HelperRequest_GetParamsString(std::list<std::string>& Params, bool Separate, std::string& Result);
	void HelperRequest_GetSignature(std::list<std::string>& Params, std::string& Result, FacebookAsyncUser* pUser);	
	void Helper_PrepareAPICall(const std::string& Method, std::list<std::string>& Params, FacebookAsyncUser* pUser);

	void Helper_PrepareAPICallEx(const std::string& Method, std::list<std::string>& Params, std::list<std::string>& NormalizedParams, FacebookAsyncUser* pUser);


	void Helper_ParseProfileInfo(XMLNode& UserInfoNode, FacebookProfileInfo& Result);
	void Helper_PrepareGetPhotoSizeCall(FacebookAsyncUser* pUser, const std::string& PhotoURL);
	void Helper_ParsePhotoSize(const std::string& RequestResult, FacebookPhotoSize& ResultSize);
	void Helper_PrepareDownloadPhotoRequest(FacebookAsyncUser* pUser, const std::string& PhotoURL);
	void Helper_BuildAppAndNonAppVectors(FacebookAsyncUser* pUser);

	void Helper_FillInfoFieldFromProfile(FACEBOOK_PROFILEINFO InfoField, const FacebookProfileInfo& ProfileInfo, std::string& Result);

	void Helper_DecodeScriptedURL(const std::string& RequestResult, std::string& Result);

	//getting global data methods
	const std::string& GetGlobalData_AppKey() {return m_AppKey;};
	const std::string& GetGlobalData_AppSecret() {return m_AppSecret;};
	const std::string& GetGlobalData_ApiServer() {return m_ApiServer;};
	const std::string& GetGlobalData_LoginURL() {return m_LoginURL;};

protected:

	//METHODS

    /// Not implemented to prevent copying
    FacebookAsync( const FacebookAsync & );     
    FacebookAsync& operator=( const FacebookAsync & );

	//callback func pointer
	FacebookCallbackFunc		m_pCallbackFunc;


	//main data

	std::string					m_AppKey;
	std::string					m_AppSecret;
	std::string					m_ApiServer;
	std::string					m_SecureApiServer;
	std::string					m_LoginURL;

	int							m_CallID;

	//error description
	std::string					m_ErrorDescription;
	bool						m_FacebookSiteChanged;
	bool						m_InvalidLoginPassword;

	std::string					m_LastFailedRequest;

	//users
	std::list<FacebookAsyncUser*>	m_UsersList;
	FacebookAsyncUser*				m_pCurUser;

};//class FacebookAsync













#endif